#!/usr/bin/env python
# -*- coding: utf-8 -*-
# vim: set fileencoding=utf-8 :



"""DocString
python-mysqldb

Este es un script que nos permite gestionar la alta, baja y modificación
de los usuarios del servicio de ftp formado por ftpd + mysql.
Este script genera una consola con las principales acciones.

"""
#Para tener logs de depuración
from modLog import *
from modMySQL import *

import re
import os
import sys
import cmd
import getpass

#Valor por defecto del MySQL
MYSQL_DB = 'email'
MYSQL_USER = 'correo'
MYSQL_HOST = 'localhost'

#Localización del dominio /server/vmail/dominio/user
PATH_VMAIL = ''

#Este es raiz. Utilizaremos la variable log para crear logs
log = logging.getLogger(MYAPP)


class ConsolaOrdenes(cmd.Cmd):
    """Simple command processor example."""
   
    prompt = '@\>: '
    intro = """
    ____________________________________________    
    
       Menú de gestión del servidor de correo
    ____________________________________________
    """

#    doc_header = 'doc_header'
#    misc_header = 'misc_header'
#    undoc_header = 'undoc_header'
    
#    ruler = '-'
   
    last_output = ''
    __conn = ''
    
    def preloop(self):
        """Activamos la conexión a la base de datos"""
        try:
            self.__conn = MySQLdb.connect (host = MYSQL_HOST,
                              user = MYSQL_USER,
                             passwd = PASS_MYSQL,
                             db = MYSQL_DB)
        except MySQLdb.Error, error:
            print "Error  %d: %s" % (error.args[0], error.args[1])
            sys.exit (1)
        print "Conexión establecida con la base de datos"
    
    def shell(self, line):
        "Run a shell command"
        print "Running shell command:", line
        output = os.popen(line).read()
        print output
        self.last_output = output



    """
    def do_del_domain(self, domain_name):
        if not domain_name:
            self.do_list_accounts('')
            idCuenta = raw_input('Id de la cuenta a borrar: ')
        if self.confirm('¿Quiere borrar la cuenta con id='+idCuenta+'?: '):
            account_name = self.get_accountname(idCuenta)    
            if account_name:
                sql = 'DELETE FROM accounts WHERE id='+idCuenta
                num = execute(self.__conn,sql)
                self.__conn.commit()
                log.info("Se ha borrado la cuenta de FTP del MySQL:"+account_name)
                print "Se ha borrado: %d registro" % num 
                if self.confirm('¿Quiere que se borre el contenido y el directorio?: '):
                    orden = 'rm -fr '+PATH_VSFTP+account_name
                    self.shell(orden)
                    log.info("Se ha borrado la cuenta de FTP del MySQL:"+account_name)
                else:
                    print "El directorio "+PATH_VSFTP+account_name+" no ha sido borrado"
        else:
            print "No existe ninguna cuenta con el Id indicado"
    
    def help_del_account(self):
        print '\n'.join([ 'del_account [id]',
            'Borra la cuenta con el id pasado o pregunta por el id',
            ])

    """
    
    def do_add_domain(self, domain_name):
        """
        Añade un nuevo dominio a la base de datos
        IN: [op]domain_name{Nombre del nuevo dominio}
        OUT:
        """
        description = ''
        if not domain_name:
            domain_name = raw_input('Nombre del nuevo dominio: ')
        if not self.validateDomain(domain_name):
            print "La estructura del dominio no es correcta [dominio.xxx]"
        else:
            if not self.get_domain_id(domain_name):
                description = raw_input('Descripción del dominio:')
                sql = 'INSERT INTO virtual_domains (dominio, description) VALUES (\'' + domain_name + '\',\'' + description +'\')'
                print sql
                num = execute(self.__conn, sql)
                self.__conn.commit ()
                print "Se ha creado: %d registro" % num 
                log.info("Se ha creado el dominio:" + domain_name)
            else:
                print "El dominio ya existe"

    def help_add_email(self):
        print '\n'.join([ 'add_email [nombre_cuenta]',
            'Crea una cuenta con el nombre pasado o pregunta por el',
            ])

    def help_change_pw(self):
        print '\n'.join([ 'change_pw [nombre_cuenta]',
            'Cambia la contraseña de una cuenta de email dada.',
            ])
    
    def do_change_pw(self, email):
        """
        Modifica la contraseña de un email
        IN: [op]email{ cuenta a modificar la contraseña -> email@dominio.com }
        OUT:
        """
        if not email:
            email = raw_input('Email a cambiar contraseña [usuario@dominio.xxx]: ')
        if not self.validateEmail(email):
            print "Estructura del email no es correcta [usuario@dominio.xxx]"
        else:  
            id_email = self.__get_ids_email(email)
            if id_email: 
                email_pass = self.confrim_password()
                sql = 'UPDATE virtual_users SET password = SHA1(\'' + email_pass + '\') WHERE idUser=' + id_email['idUser'] + ' AND idDomain=' + id_email['idDomain'] 
                num = execute(self.__conn, sql)
                self.__conn.commit ()
                print "Se ha modificado: %d registro" % num 
                log.info("Se ha cambiado la contraseña del email:" + email)
            else:    
                print "El email no existe."

    def do_del_email(self, listaEmail):
        """
        Borra un email o una lista de emails.
        USO: del_email email1 email2 email3 ...
        """    
        if not listaEmail:
            print "Tienes que especificar un email al menos"
            return False
        for email in listaEmail.split():
            if self.confirm("Estas seguro que quieres borrar el email " + email + " [si o no]: "):
                id_email= self.__get_ids_email(email)
                if id_email:
                    if self.__del_email(id_email['idUser'],id_email['idDomain']):
                        print "Se ha borrado el email:" + email
                        log.info("Se ha borrado el email:" + email)
                    else:
                        print "Se ha producido algún error"
                else:
                    print "El email indicado para borrar no existe"
            else:
                print "Se ha cancelado el borrado del email"

    def __del_email(self, idUser, idDomain):
        """
        Función que borra un email de la base de datos
        IN: idUsuario e idDominio de la tabla virtual_users
        OUT: número de elementos borrados
        """
        sql='DELETE FROM virtual_users WHERE idDomain=' + idDomain + ' AND idUser=' + idUser
        num = execute(self.__conn, sql)
        self.__conn.commit ()
        return num 

    def __get_ids_email(self, email):
        """
        Función que obtiene el idUser y el idDomain que definen un email
        IN: email
        OUT: Si no existe devuelve False si existe un diccionarios con
            el idDomain y el idUser
        """
        if not self.validateEmail(email):
            print "Estructura del email no es correcta [usuario@dominio.xxx]"
        else:   
            email = email.split('@')
            id_dominio = self.get_domain_id(email[1])
            if not id_dominio:
                print "El dominio del email no existe."
            else:
                id_email = self.get_email_id(id_dominio, email[0])
                if id_email:
                    return {'idDomain':str(id_dominio), 'idUser':str(id_email)}
        return False         
        

    def do_add_alias(self, listaEmail):
        """
        Añade un nuevo alias de correo
        IN: add_alias email1 email2 email3 ...
        """
        if not listaEmail:
            print "Tienes que especificar un alias"
            return False

        l = listaEmail.split()
        if len(l)<2:
            print "Tienes que especificar una o más direcciones de email"
            return False

        print "Alias: %s" % l[0]
        print "Se expande por:"
        for mail in l[1:]:
            print '-> %s %s' % (mail, self.validateEmail(mail))
            if not self.validateEmail(mail):
                print " %s no es una dirección de email válida" % mail
                return False
        #Extraemos el dominio y obtenemos su id
	    alias = l[0].split('@')
        id_dominio = self.get_domain_id(alias[1])
        if not id_dominio:
        	print "El dominio del alias de email no existe"
        else:
	        #Vemos si el alias ya existe
            id_alias = self.get_alias_id(id_dominio, alias[0])
            if not id_alias:
                print "El alias no existe y hay que crearlo"
                self.add_alias(id_dominio, alias[0])
            id_alias = self.get_alias_id(id_dominio, alias[0])
            for email in l[1:]:
                self.add_email2alias(id_alias, email)

    def add_alias(self, id_domain, alias):
        """
        Añade un nuevo alias a un dominio
        """
        description = raw_input('Descripción del alias: ')
        sql = 'INSERT INTO virtual_aliases (idAlias,idDomain,alias,description) VALUES (\'\',' + str(id_domain) + ',\'' + alias + '\', \'' + description + '\')'    
        num = execute(self.__conn, sql)
        self.__conn.commit ()
        print "Se ha creado: %d registro" % num 
        log.info("Se ha creado el alias:" + alias)

    def add_email2alias(self, id_alias, email):
        """
        Añade un nuevo alias a un dominio
        """
        sql = 'INSERT INTO emailAlias (idAlias, email) VALUES (' + str(id_alias) + ', \'' + email + '\')'    
        num = execute(self.__conn, sql)
        self.__conn.commit ()
        print "Se ha creado: %d registro" % num 
        log.info("Se ha añadido un email al alias:" + email)
    
    def do_add_email(self, email):
        """
        Añade un nuevo email a la base de datos
        IN: [op]email{ Nuevo email -> email@dominio.com }
        OUT:
        """
        description = ''
        email_pass = ''
        vacationActive = 0
        vacationText = ''
        if not email:
            email = raw_input('Email a crear [usuario@dominio.xxx]: ')
        if not self.validateEmail(email):
            print "Estructura del email no es correcta [usuario@dominio.xxx]"
        else:   
            email = email.split('@')
            id_dominio = self.get_domain_id(email[1])
            if not id_dominio:
                print "El dominio del email no existe"
            else:
                if not self.get_email_id(id_dominio, email[0]):
                    print "Se puede crear email"
                    description = raw_input('Descripción del email:')
                    email_pass = self.confrim_password()
                    sql = 'INSERT INTO virtual_users (idUser,idDomain,description,password,user,vacationActive,vacationText) VALUES (\'\',' + str(id_dominio) + ',\'' + description + '\',SHA1(\'' + email_pass + '\'),\'' + email[0] + '\',0,\'\')'        
                    num = execute(self.__conn, sql)
                    self.__conn.commit ()
                    print "Se ha creado: %d registro" % num 
                    log.info("Se ha creado el email:" + email[0] + '@' + email[1])
                else:    
                    print "El usuario ya existe en el dominio"
    
    def help_add_email(self):
        print '\n'.join([ 'add_email [email]',
            'Crea un email nuevo pasándoselo como argumento o pregunta por el',
            ])
    
    def do_quit(self, line):
        """
        Cierra la conexión con la BBDD y finaliza la ejecución de la shell
        IN: 
        OUT:
        """
        try:
            close(self.__conn)
        except MySQLdb.Error, error:
            print "Error  %d: %s" % (error.args[0], error.args[1])
        print "Conexión cerrada con la base de datos"
        return True

    def help_quit(self):
        print '\n'.join([ 'quit',
            ' Escribe quit para salir',
            ])

    def help_help(self):
        print '\n'.join([ 'Uso: help [Comando]',
            'Muestra información de ayuda',
            ])
  

    def do_list_domains(self, line):
        """
        Obtiene y saca por pantalla los dominios existentes
        IN: 
        OUT:
        """
        domains = self.get_domains()
        self.print_domains(domains)

    def print_domains(self, dominios):
        """
        Sacar por pantalla un listado de los dominios exitentes
        IN: 
        OUT: 
        """
        print ''' 
                          Dominios Activos 
---------------------------------------------------------------------
  nº           Dominio           Descripción        '''
        for i in range(len(dominios))       :
            print "%3d  %20s  -->  %s" % (i+1, dominios[i]['dominio'], dominios[i]['description'])
        print '\n'
    
    def get_domains(self):
        """
        Función que obtiene los dominios y su descripción
        IN:
        OUT: variable con el listado de los dominio y su descripción
        """
        sql = 'SELECT dominio,description FROM virtual_domains order by dominio'
        filas = select(self.__conn, sql)
        return filas

    def help_list_emails(self):
        print '\n'.join([ '----- HELP: list_emails -----',
            'Lista las cuentas de email existentes para un dominio.'\
            +'\nARG1 (op): Dominio a listar.\nEjemplo: list_emails dominio.com\n',
            ])
        
    def do_list_emails(self, dominio):
        """
        Función que obtiene los y emails de un dominios. El dominios puede
        ser dado como argumento. En el caso de no darlo, se listan los dominios
        existentes y te permite elegir el dominio a listar.
        IN: [op]dominio
        OUT: 
        """
        if not dominio:
            domains = self.get_domains()
            self.print_domains(domains)
            n_dominio = raw_input('Elija el número del dominio a visualizar:')
            dominio = domains[int(n_dominio)-1]['dominio']
        self.print_emails(self.get_emails(dominio))
        
    def get_emails(self, dominio):
        sql = 'SELECT u.* from virtual_users u, virtual_domains d where u.idDomain=d.idDomain and dominio=\'' + dominio + '\' order by u.user'
        filas = select(self.__conn, sql)
        return filas

    def print_emails(self, emails):
        print ''' 
                       Emails del dominio 
---------------------------------------------------------------------'''
        for i in range(len(emails)):
            print "%3d ||  %-15s  || %-30s  ||  %-2s  ||  %-25s  ||" % (i+1, emails[i]['user'], \
emails[i]['description'], emails[i]['vacationActive'], emails[i]['vacationText'])
        print '\n'
        
    def confirm(self, mensaje, mensaje_info = "si o no"):
        while 1: 
            respuesta = raw_input(mensaje)
            if respuesta in ('s', 'si', 'sí', 'yes', 'y'): return True
            elif respuesta in ('n', 'no'): return False
            else: print mensaje_info 

    def confrim_password(self):
        while 1: 
            password1 = getpass.unix_getpass("Contraseña de la nueva cuenta:")
            password2 = getpass.unix_getpass("Repetir contraseña:")
            if password1 == password2:
                return password1
            else:
                print "Las contraseñas no coinciden"
    
    def get_user_email(self, idemail, id_domain):
        sql = 'SELECT user, description FROM virtual_users WHERE idUser='\
             + idemail+' and idDomain=' + id_domain
        filas = select(self.__conn, sql)
        return filas[0]['username']        
    
    def get_alias_id(self, id_domain, alias_name):
        sql = 'SELECT idAlias FROM virtual_aliases Where idDomain=' + str(id_domain) + '  AND alias=\'' + alias_name +'\''
        filas = select(self.__conn, sql)
        if filas:
            return filas[0]['idAlias']        
        else:
            return False 

    def get_domain_id(self, domain_name):
        sql = 'SELECT idDomain FROM virtual_domains WHERE dominio like \''+domain_name+'\''
        filas = select(self.__conn, sql)
        if filas:
            return filas[0]['idDomain']        
        else:
            return False 
    
    def get_email_id(self, id_domain, user):
        """
        Función que obtiene el id del email indicado
        IN: idDomain, usuario del email
        OUT: Si lo encuentra devuelve el idUser en caso contrario False
    
        """
        sql = 'SELECT idUser FROM virtual_users WHERE idDomain=' + str(id_domain) +' AND user=\'' + user + '\'' 
        filas = select(self.__conn, sql)
        if filas:
            return filas[0]['idUser']        
        else:
            return False 
    
    def validateEmail(self, email):
        if len(email) > 7:
            if re.match("^.+\\@(\\[?)[a-zA-Z0-9\\-\\.]+\\.([a-zA-Z]{2,3}|[0-9]{1,3})(\\]?)$", email) != None:
                return True
        return False
    
    def validateDomain(self, domain):
        if len(domain) > 4:
            if re.match("(\\[?)[a-zA-Z0-9\\-\\.]+\\.([a-zA-Z]{2,3}|[0-9]{1,3})(\\]?)$", domain) != None:
                return True
        return False

if __name__ == '__main__':
    USER = raw_input('Usuario de acceso a MySQL['+MYSQL_USER+']: ')
    if USER:
        MYSQL_USER = USER
    PASS_MYSQL = getpass.unix_getpass("Password de acceso a MySQL: ")

    ConsolaOrdenes ().cmdloop()

__version__ = "$Revision$"
# $Source$

